package net.gdface.cassdk;

import static net.gdface.utils.BufferUtils.*;

import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.EyeInfo;
import net.gdface.sdk.FAngle;
import net.gdface.sdk.FInt2;
import net.gdface.sdk.FRect;
import net.gdface.utils.Assert;
import static net.gdface.cassdk.CasAndroidConfigProvider.*;

/**
 * 人脸检测信息描述对象<br>
 * 实现原始人脸检测数据(double[])的解析，以及与{@link CodeInfo}实例的相互转换
 * @author guyadong
 *
 */
public class NativeFaceInfo extends CodeInfo {
	/**
	 * 人脸检测数据(double数组)的字段名索引
	 * @author guyadong
	 *
	 */
	enum FieldIndex{
		/* 人脸位置矩形左上角 x */FRECT_LEFT,
		/* 人脸位置矩形左上角 y */FRECT_TOP,
		/* 人脸位置矩形宽度 */FRECT_WIDTH,
		/* 人脸位置矩形高度 */FRECT_HEIGHT,
		/* 质量评价 0-1 1 为质量最高 高分辨率建议阈值 0.5 低分辨率建议阈值 0.3，与模糊度与亮度相关 */FD_QUALITY,
		/* 翻滚角，绕 z 轴旋转的角度，-90°~ +90°, 左为负，右为正（观察者视角）*/FANGLE_ROLL,
		/* 偏航角，绕 y 轴旋转的角度，-90°~ +90°, 左为负，右为正（观察者视角）*/FANGLE_YAW,
		/* 俯仰角，绕 x 轴旋转的角度， -90°~ +90°, 上为正，下为负（观察者视角）*/FANGLE_PITCH,
		/* 清晰度，0-100 越大越清晰 */FD_CLARITY,
		/* 明亮度，0-255 越大亮度越高 */FD_BRIGHTNESS,
		/* 人脸关键点 0 x坐标:左眼皮上端 */FD_POINT0_X,
		/* 人脸关键点 0 y坐标:左眼皮上端 */FD_POINT0_Y,
		/* 人脸关键点 1 x坐标:右眼皮上端 */FD_POINT1_X,
		/* 人脸关键点 1 y坐标:右眼皮上端 */FD_POINT1_Y,
		FD_POINT2_X,
		FD_POINT2_Y,
		FD_POINT3_X,
		FD_POINT3_Y,
		FD_POINT4_X,
		FD_POINT4_Y,
		FD_POINT5_X,
		FD_POINT5_Y,
		FD_POINT6_X,
		FD_POINT6_Y,
		/* 人脸关键点 7 x坐标:鼻尖 */FD_POINT7_X,
		/* 人脸关键点 7 y坐标:鼻尖 */FD_POINT7_Y,
		FD_POINT8_X,
		FD_POINT8_Y,
		FD_POINT9_X,
		FD_POINT9_Y,
		/* 人脸关键点 10 x坐标:左嘴角 */FD_POINT10_X,
		/* 人脸关键点 10 y坐标:左嘴角 */FD_POINT10_Y,
		/* 人脸关键点 11 x坐标:右嘴角 */FD_POINT11_X,
		/* 人脸关键点 11 y坐标:右嘴角 */FD_POINT11_Y,
		FD_POINT12_X,
		FD_POINT12_Y,
		FD_POINT13_X,
		FD_POINT13_Y,
		FD_POINT14_X,
		FD_POINT14_Y,
		FD_POINT15_X,
		FD_POINT15_Y,
		/* 人脸关键点 16 x坐标:左眼内角 */FD_POINT16_X,
		/* 人脸关键点 16 y坐标:左眼内角 */FD_POINT16_Y,
		/* 人脸关键点 17 x坐标:左眼皮下端 */FD_POINT17_X,
		/* 人脸关键点 17 y坐标:左眼皮下端 */FD_POINT17_Y,
		/* 人脸关键点 18 x坐标:左眼外角 */FD_POINT18_X,
		/* 人脸关键点 18 y坐标:左眼外角 */FD_POINT18_Y,
		/* 人脸关键点 19 x坐标:右眼外角 */FD_POINT19_X,
		/* 人脸关键点 19 y坐标:右眼外角 */FD_POINT19_Y,
		/* 人脸关键点 20 x坐标:右眼皮下端 */FD_POINT20_X,
		/* 人脸关键点 20 y坐标:右眼皮下端 */FD_POINT20_Y,
		/* 人脸关键点 21 x坐标:右眼内角 */FD_POINT21_X,
		/* 人脸关键点 21 y坐标:右眼内角 */FD_POINT21_Y,
		FD_POINT22_X,
		FD_POINT22_Y,
		FD_POINT23_X,
		FD_POINT23_Y,
		FD_POINT24_X,
		FD_POINT24_Y,
		FD_CONFIDENCE
	}
	private static final long serialVersionUID = 1L;
	/**
	 * 质量评价 0-1 1 为质量最高 高分辨率建议阈值 0.5 低分辨率建议阈值 0.3，与模糊度与亮度相关
	 */
	double quality;
	/**
	 * 0-100 越大越清晰
	 */
	int clarity;
	/**
	 * 明亮度，0-255 越大亮度越高
	 */
	int brightness;
	/**
	 * 人脸检测框置信度(与{@link FAngle#getConfidence()}相同)
	 */
	double confidence;
	/**
	 * 从JNI动态库返回的原始人脸检测数据 double[61]
	 */
	double nativeData[];
	public NativeFaceInfo(double[] nativeData) {
		this(nativeData,0);
	}
	/**
	 * 从原始人脸信息数组构造实例
	 * @param nativeData 原始数据
	 * @param start 数据起始索引
	 */
	public NativeFaceInfo(double[] nativeData,int start) {
		super();
		init(nativeData, start);
	}
	public NativeFaceInfo(CodeInfo codeInfo) {
		super();
		Assert.notNull(codeInfo, "codeinfo");
		byte[] facialData = codeInfo.getFacialData();
		Assert.notNull(facialData, "facialData");
		init(asDoubleArray(facialData), 0);
	}
	/**
	 * 从原始人脸检测数据(61个double)解析为当前对象对应字段
	 * @param nativeData
	 * @param start
	 */
	private void init(double[] nativeData, int start){
		Assert.isTrue(start>=0,"start>=0", "invalid start");
		Assert.isTrue(nativeData!=null && nativeData.length-start>=FDDATA_LEN, 
				"nativeData!=null && nativeData.length-start>=CaffeMobile.FDDATA_LEN","invalid nativeData");
		this.nativeData = new double[FDDATA_LEN];
		System.arraycopy(nativeData, start, this.nativeData, 0, this.nativeData.length);
		////////////////////
		FRect pos = getPos();
		if(pos == null){
			pos = new FRect();
		}
		pos.setLeft((int) this.nativeData[FieldIndex.FRECT_LEFT.ordinal()]);
		pos.setTop((int) this.nativeData[FieldIndex.FRECT_TOP.ordinal()]);
		pos.setWidth((int) this.nativeData[FieldIndex.FRECT_WIDTH.ordinal()]);
		pos.setHeight((int) this.nativeData[FieldIndex.FRECT_HEIGHT.ordinal()]);
		setPos(pos);
		/////////////////////////
		FAngle angle = getAngle();
		if(angle == null){
			angle = new FAngle();
		}
		angle.setRoll((int)this.nativeData[FieldIndex.FANGLE_ROLL.ordinal()]);
		angle.setYaw((int)this.nativeData[FieldIndex.FANGLE_YAW.ordinal()]);
		angle.setPitch((int)this.nativeData[FieldIndex.FANGLE_PITCH.ordinal()]);
		angle.setConfidence((int)(this.nativeData[FieldIndex.FD_CONFIDENCE.ordinal()]*100));
		setAngle(angle);
		//////////////////////////
		EyeInfo ei = getEi();
		if(ei == null){
			ei = new EyeInfo();
		}
		{
			// 计算左右眼中间点位置 
			double lx = (this.nativeData[FieldIndex.FD_POINT0_X.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT16_X.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT17_X.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT18_X.ordinal()])/4;
			double ly = (this.nativeData[FieldIndex.FD_POINT0_Y.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT16_Y.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT17_Y.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT18_Y.ordinal()])/4;
			double rx = (this.nativeData[FieldIndex.FD_POINT1_X.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT19_X.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT20_X.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT21_X.ordinal()])/4;
			double ry = (this.nativeData[FieldIndex.FD_POINT1_Y.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT19_Y.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT20_Y.ordinal()] 
								+ this.nativeData[FieldIndex.FD_POINT21_Y.ordinal()])/4;
			ei.setLeftx((int)lx);
			ei.setLefty((int)ly);
			ei.setRightx((int)rx);
			ei.setRighty((int)ry);
			setEi(ei);
		}
		//////////////////////////
		FInt2 mouth = getMouth();
		if(mouth == null){
			mouth = new FInt2();
		}
		mouth.setX((int)this.nativeData[FieldIndex.FD_POINT14_X.ordinal()]);
		mouth.setY((int)this.nativeData[FieldIndex.FD_POINT14_Y.ordinal()]);
		setMouth(mouth);
		////////////////////////
		FInt2 nose = getNose();
		if(nose == null){
			nose = new FInt2();
		}
		nose.setX((int)this.nativeData[FieldIndex.FD_POINT7_X.ordinal()]);
		nose.setY((int)this.nativeData[FieldIndex.FD_POINT7_Y.ordinal()]);
		setNose(nose);
		//////////////////////
		quality        = this.nativeData[FieldIndex.FD_QUALITY.ordinal()];
		clarity         = (int) this.nativeData[FieldIndex.FD_CLARITY.ordinal()];
		brightness  = (int) this.nativeData[FieldIndex.FD_BRIGHTNESS.ordinal()];
		confidence = this.nativeData[FieldIndex.FD_CONFIDENCE.ordinal()];
		setFacialData(asByteArray(this.nativeData));
	}
	/**
	 * {@link CodeInfo} 转换为{@link NativeFaceInfo}
	 * @param code
	 * @return
	 */
	public static NativeFaceInfo toNative(CodeInfo code){
		if(code == null){
			return null;
		}			
		if(code instanceof NativeFaceInfo){
			return (NativeFaceInfo)code;
		}
		return new NativeFaceInfo(code);
	}
	public double getQuality() {
		return quality;
	}
	public int getClarity() {
		return clarity;
	}
	public int getBrightness() {
		return brightness;
	}

	public double getConfidence() {
		return confidence;
	}
	public double[] getNativeData() {
		return nativeData;
	}
}
